/* This file is part of Clementine.
   Copyright 2013, Hans Oesterholt <debian@oesterholt.net>

   Clementine is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   Clementine is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with Clementine.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "segmenter.h"

#include "core/logging.h"
#include "core/song.h"
#include "core/timeconstants.h"

#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QString>
#include <QTemporaryFile>

//////////////////////////////////////////////////////////////////////////////
// Forward declarations of functions we need to split out segments for
// different file formats. 
//////////////////////////////////////////////////////////////////////////////

// Currently we have only libmp3splt functions
static bool MakeSegmentMp3Splt(const QString& source_file, const Song& metadata, 
                                      QString& result_file);
static bool HaveMp3Splt();


//////////////////////////////////////////////////////////////////////////////
// Class implementation of Segmenter class. 
//////////////////////////////////////////////////////////////////////////////

Segmenter::Segmenter(const QString& source_file, const Song& metadata) 
  : source_(source_file),
    metadata_(metadata) {
}

Segmenter::Type Segmenter::source_type() const
{
  Song::FileType type = metadata_.filetype();
  switch (type) {
    case Song::FileType::Type_Mpeg:       return Type::MP3;
    case Song::FileType::Type_OggVorbis:  return Type::OGGVorbis;
    default: return Type::UNSUPPORTED;
  }
}

bool Segmenter::CanSegment() 
{
  Segmenter::Type type = source_type();
  switch (type) {
    case Type::MP3:
    case Type::OGGVorbis:     return HaveMp3Splt();
    default:                  return false;
  }
}

bool Segmenter::Create() 
{
  Segmenter::Type type = source_type();
  switch (type) {
    case Type::MP3:      
    case Type::OGGVorbis: return MakeSegmentMp3Splt(source_, metadata_, segment_file_);
    default:              return false;
  }
}

QString Segmenter::created_filename() const 
{
  return segment_file_;
}


//////////////////////////////////////////////////////////////////////////////
// MP3Split functions needed to split out segments. Encapsulated in this 
// module, not visible to the outside world.
//////////////////////////////////////////////////////////////////////////////

#ifdef HAVE_MP3SPLT
// We have some libmp3splt, but do we have the right one?
extern "C" {
  #include <libmp3splt/mp3splt.h>
#ifndef SPLT_PACKAGE_VERSION  
  #include <libmp3splt/version.h>
#endif  
}

  #ifdef LIBMP3SPLT_MAJOR_VERSION
// now we have at least 0.8.2 of libmp3splt.
// because this define was introduced in that version
    #define HAVE_RIGHT_MP3SPLT
  #endif
#endif


#ifdef HAVE_RIGHT_MP3SPLT

static splt_code ReturnError(splt_state *state, splt_code error) {
  if (state != NULL)  
        mp3splt_free_state(state);
  qLog(Error) << "libmp3splt returned error code " << error;
  return false;
}

static bool MakeSegmentMp3Splt(const QString& source_file, const Song& metadata, 
                                      QString& result_file) 
{
  // Create a temporary file to use with libmp3splt  
  QString templ(QDir::tempPath()+"/clementine_XXXXXX."+QFileInfo(source_file).suffix().toLower());
  QTemporaryFile file(templ);
  file.open();
  file.close();
  result_file=file.fileName();
  file.remove();

  qLog(Debug) << "Creating temp file for the segment we need " << QFileInfo(result_file).baseName();


  // Get the segment to split out of the source  
  int begin_offset_in_hs = metadata.beginning_nanosec() / kNsecPerHsec; 
  int end_offset_in_hs = -1;
  if (metadata.end_nanosec() >= 0)  {
    end_offset_in_hs = metadata.end_nanosec() / kNsecPerHsec;
  }

  // Do the splitting work

  splt_code error = SPLT_OK;

  // Creating state and find the plugins
  splt_state *state = mp3splt_new_state(&error);
  if (error < 0) return ReturnError(state, error); // checked the code, works fine.
  
  error = mp3splt_find_plugins(state);
  if (error < 0) return ReturnError(state, error);
  
  // Set the source file and output file
  error = mp3splt_set_filename_to_split(state, source_file.toUtf8().constData());
  if (error < 0) return ReturnError(state, error);
  
  error = mp3splt_set_path_of_split(state, QDir::tempPath().toUtf8().constData());
  if (error < 0) return ReturnError(state, error);
  
  error = mp3splt_set_int_option(state, SPLT_OPT_OUTPUT_FILENAMES, SPLT_OUTPUT_CUSTOM);
  if (error < 0) return ReturnError(state, error);
  
  // Make splitpoints
  QString output_basename = QFileInfo(result_file).baseName();
  
  splt_point *point = mp3splt_point_new(begin_offset_in_hs, &error);
  if (error < 0) return ReturnError(state, error);
  error = mp3splt_point_set_type(point, SPLT_SPLITPOINT);
  if (error < 0) return ReturnError(state, error);
  error = mp2splt_point_set_name(point, output_basename.toUtf8().constData());
  if (error < 0) return ReturnError(state, error);
  error = mp3splt_append_splitpoint(state, point);
  if (error < 0) return ReturnError(state, error);
  
  splt_point *skip = mp3splt_point_new(end_offset_in_hs, &error);
  if (error < 0) return ReturnError(state, error);
  error = mp3splt_point_set_type(skip, SPLT_SKIPPOINT);
  if (error < 0) return ReturnError(state, error);
  error = mp3splt_append_splitpoint(state, skip);
  if (error < 0) return ReturnError(state, error);
  
  // Append cuesheet tags and merge with existing
  splt_tags *tags = mp3splt_tags_new(&error);
  if (error < 0) return ReturnError(state, error);
  
  error = mp3splt_read_original_tags(state);
  if (error < 0) return ReturnError(state, error);

  error = mp3splt_tags_set(tags,
                  SPLT_TAGS_TITLE, metadata.title().toUtf8().constData(),
                  SPLT_TAGS_ARTIST, metadata.artist().toUtf8().constData(),
                  SPLT_TAGS_ALBUM, metadata.album().toUtf8().constData(),
                  SPLT_TAGS_PERFORMER, metadata.albumartist().toUtf8().constData(),
                  SPLT_TAGS_YEAR, QString().sprintf("%d",metadata.year()).toUtf8().constData(),
                  SPLT_TAGS_COMMENT, metadata.coment().toUtf8().constData(),
                  SPLT_TAGS_GENRE, metadata.genre().toUtf8().constData(),
                  SPLT_TAGS_TRACK, QString().sprintf("%d",metadata.track()).toUtf8().constData()
                  );
  if (error < 0) return ReturnError(state, error);
  
  error = mp3splt_append_tags(state, tags);
  if (error < 0) return ReturnError(state, error);

  // Split the segment out
  error = mp3splt_split(state);
  if (error < 0) return ReturnError(state, error);
  
  // We handle errors from the split itself later

  int error = mp3splt_free_state(state);
  if (error < 0) return ReturnError(state, error);

  return true;
}

bool HaveMp3Splt()
{
  return true;
}

#else // No libmp3splt of the right version (>0.8)

static bool MakeSegmentMp3Splt(const QString& source_file, const Song& metadata, 
                                      QString& result_file) 
{
  return false;
}

static bool HaveMp3Splt() 
{
  return false;
}

#endif 


